package cPanes;


import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;

import javax.swing.JMenu;
import javax.swing.JMenuItem;
import javax.swing.JTextPane;
import javax.swing.text.AttributeSet;
import javax.swing.text.BadLocationException;
import javax.swing.text.Style;
import javax.swing.text.StyleConstants;
import javax.swing.text.StyleContext;
import javax.swing.text.StyledDocument;


import uiComponents.ContextMenu;
/**
 * EquPane is a special type of TextPane, designed to hold equations. It supports subscript and superscript, and a limited selection
 * of greek characters.
 */
public class EquPane extends TextPane implements KeyListener{
	private boolean capture=false;
	private String buffer;
	private String[][] table;
	private Style[] styles;
	private int style;
	private JMenuItem[] formatting;
	private JMenuItem fixit;
	/**
	 * The standard constructor, sets up the menus and the table of special characters.
	 */
	public EquPane(){
		super();
		this.setName("EQU");
		menuSetup();
		style=0;
		this.getTextArea().addKeyListener(this);
		tableSetup();
		styleSetup();
	}
	/**
	 * Handles changing the style of a selection
	 */
	public void menuActions(ActionEvent e){
		Object o = e.getSource();
		if (o.equals(fixit)){
			parseFormatting(this.getTextArea().getStyledDocument());
		}
		else{
			for(int i=0; i<formatting.length; i++){
				if (o.equals(formatting[i])){
					JTextPane t= this.getTextArea();
					StyledDocument doc = t.getStyledDocument();
					int start = t.getSelectionStart();
					int end = t.getSelectionEnd();
					doc.setCharacterAttributes(start, end-start, styles[i], true);
				}
			}
		}
	}
	/**
	 * Sets up a menu containing the formatting options
	 */
	private void menuSetup(){
		//The formatting menu
		ContextMenu c = this.getMenu();
		JMenu format = new JMenu("Formatting");
		formatting = new JMenuItem[3];
		formatting[0]= new JMenuItem("Superscript");
		formatting[1]= new JMenuItem("Normal");
		formatting[2]= new JMenuItem("Subscript");
		for (int i=0; i<formatting.length; i++){
			format.add(formatting[i]);
			formatting[i].addActionListener(c);
		}
		//The "fix-it" button
		fixit = new JMenuItem("Fix formatting");
		fixit.addActionListener(c);
		//Add both to the ContextMenu
		c.add(fixit);
		c.add(format);
		
	}
	/**
	 * Creates the styles (subscript, superscript and regular) which can be applied to text.
	 */
	private void styleSetup(){
		styles = new Style[3];
		StyledDocument doc = this.getTextArea().getStyledDocument();
	    Style def = StyleContext.getDefaultStyleContext().getStyle( StyleContext.DEFAULT_STYLE );
	    styles[0] = doc.addStyle( "regular", def );
	    styles[1] = doc.addStyle( "superscript", styles[0]);
	    StyleConstants.setSuperscript( styles[1], true );
	    styles[2] = doc.addStyle( "superscript", styles[0]);
	    StyleConstants.setSubscript( styles[2], true );
	}
	/**
	 * Creates a table where [i][0] is the 'keyword' - what the user types - and [i][1] is the resulting Unicode character
	 */
	private void tableSetup(){
		table = new String[10][2];
		table[0][0]="DELTA";
		table[0][1]="\u0394";
		table[1][0]="SIGMA";
		table[1][1]="\u03A2";
		table[2][0]="PHI";
		table[2][1]="\u03A6";
		table[3][0]="OMEGA";
		table[3][1]="\u03A9";
		table[4][0]="BETA";
		table[4][1]="\u03B2";
		table[5][0]="THETA";
		table[5][1]="\u03B8";
		table[6][0]="LAMBDA";
		table[6][1]="\u03BB";
		table[7][0]="PI";
		table[7][1]="\u03C0";
		table[8][0]="MU";
		table[8][1]="\u03BC";
		table[9][0]="RO";
		table[9][1]="\u03C1";
	}

	
	public void keyPressed(KeyEvent arg0) {
		
	}

	
	public void keyReleased(KeyEvent arg0) {
		
	}

	/**
	 * Listens for special input and performs the requisite formatting.
	 */
	public void keyTyped(KeyEvent arg0) {		
		JTextPane t=this.getTextArea();
		int c= t.getCaretPosition();
		if(arg0.getKeyChar() == '<'){
			parseGreekForwards(c, arg0);
		}
		else if(arg0.getKeyChar() == '>'){
			parseGreekBackwards(c-1, arg0);
		}
		else if(arg0.getKeyChar() == '^'){
			arg0.consume();
			switchSuperScript(c);
		}
		else if(arg0.getKeyChar() == '_'){
			arg0.consume();
			switchSubScript(c);
		}
	}
	/**
	 * Searches backwards from i to find an opening bracket '<'. Inverts the characters in between and checks against the table 
	 * to make the appropriate substitution. Consumes the closing bracket if a replacement is made.
	 * @param i The index of the caret when the closing bracket was typed
	 * @param k The KeyEvent in which the closing bracket was typed
	 */
	private void parseGreekBackwards(int i, KeyEvent k){
		String t = this.getTextArea().getText();
		String buf="";
		String s="";
		boolean flag=false;
		
		while (i>=0){
			if (t.charAt(i)=='<'){
				flag=true;
				break;
			}
			buf = buf+t.charAt(i);
			i--;
		}
		String tbuf=buf;
		buf="";
		for (int j=tbuf.length()-1; j>=0; j--){
			buf = buf + tbuf.charAt(j);
		}
		if (flag){
			for (int j=0; j<table.length; j++){
				if (buf.equalsIgnoreCase(table[j][0])){
					s = table[j][1];
				}
			}
			k.consume();
			this.getTextArea().setText(t.substring(0, i) + s + t.substring(i+buf.length()+1));
		}
	}
	/**
	 * Searches forwards from i to find a closing bracket '>'. Checks the characters in between against the table and makes the appropriate
	 * substitution. Consumes the opening bracket if a replacement is made.
	 * @param i
	 * @param k
	 */
	private void parseGreekForwards(int i, KeyEvent k){
		String t = this.getTextArea().getText();
		String buf="";
		String s="";
		boolean flag=false;
		while (i<t.length()){
			if (t.charAt(i)=='>'){
				flag=true;
				break;
			}
			buf = buf+t.charAt(i);
			i++;
		}
		if (flag){
			for (int j=0; j<table.length; j++){
				if (buf.equalsIgnoreCase(table[j][0])){
					s = table[j][1];
				}
			}
			k.consume();
			this.getTextArea().setText(t.substring(0, i-buf.length()) + s + t.substring(i+1));
		}
	}
	/**
	 * Sets the style of the next character after the caret to superscript if presently regular, or regular if presently superscript.
	 * @param caret The position after the caret
	 */
	private void switchSuperScript(int caret){
		StyledDocument doc = this.getTextArea().getStyledDocument();
		if (style==1) style=0;
		else style=1;
		try {
			doc.insertString(caret, " ", styles[style]);
		} catch (BadLocationException e) {
			e.printStackTrace();
		}
	}
	/**
	 * Sets the style of the next character after the caret to subscript if presently regular, or regular if presently subscript.
	 * @param caret The position after the caret
	 */
	private void switchSubScript(int caret){
		StyledDocument doc = this.getTextArea().getStyledDocument();
		if (style==2) style=0;
		else style=2;
		try {
			doc.insertString(caret, " ", styles[style]);
		} catch (BadLocationException e) {
			e.printStackTrace();
		}
	}
	/**
	 * Parses the string s, converting ':%:' sequences into line breaks. Then sets the text area's contents accordingly.
	 */
	public void openSave(String s) {
		String temp="";
		temp = s.replaceAll(":%:", "\n");
		this.getTextArea().setText(temp);
		parseFormatting(this.getTextArea().getStyledDocument());
	}
	/**
	 * Gets text area's contents and replaces line breaks with ':%:' sequences to avoid errors while reading files.
	 */
	public String returnSave() {
		StyledDocument t = this.getTextArea().getStyledDocument();
		AttributeSet tstyle;
		String saveText="";
		int currentstyle=1;
		int charstyle=0;
		for (int i=0; i< t.getLength(); i++){
			tstyle = t.getCharacterElement(i).getAttributes();
			if (StyleConstants.isSuperscript(tstyle)) charstyle=0;
			else if (StyleConstants.isSubscript(tstyle)) charstyle=2;
			else charstyle=1;
			if (charstyle != currentstyle){
				if (charstyle == 1){
					if (currentstyle==0) {
						saveText=saveText+"^";
					}
					else if (currentstyle==2) {
						saveText=saveText+"_";
					}
					currentstyle=1;
				}
				else if (charstyle == 0){
					saveText=saveText+"^";
					currentstyle=0;
				}
				else if (charstyle == 2){
					saveText=saveText+"_";
					currentstyle=2;
				}
			}
			try {
				saveText = saveText + t.getText(i,1);
			} catch (BadLocationException e) {
				System.out.println(saveText);
				System.out.println("Bug 1. Sorry, shoot me an email explaining how you broke it. Please?");
			}
		}
		String s = saveText.replaceAll("\n", ":%:");
		return s;
	}
	
	public void parseFormatting(StyledDocument s){
		String character;
		String ts="";
		int st=0;
		try {
			ts = s.getText(0, s.getLength());
			s.remove(0, s.getLength());
			s.setCharacterAttributes(0, s.getLength(), styles[1], true);
		} catch (BadLocationException e1) {
			System.out.println("Bug 2. Sorry, shoot me an email explaining how you broke it. Please?");
		}
		try{
			for (int i=0; i<ts.length(); i++){
				character =ts.substring(i,i+1);
				//TODO I think that setting the characters' styles individually is preferable to inserting spaces everywhere. May be wrong.
				if (character.equals("^")){
					if (st==1){
						st=0;
						//s.insertString(i," ", styles[1]);
					}
					else{
						//s.insertString(i," ", styles[0]);
						st=1;
					}
				}
				else if (character.equals("_")){
					if (st==2){
						st=1;
						//s.insertString(i," ", styles[1]);
					}
					else{
						//s.insertString(i," ", styles[2]);
						st=2;
					}
				}
				else {
					s.insertString(s.getLength(), character, styles[st]);
				}
			}
		}
		catch (BadLocationException e){
			System.out.println(e.getMessage());
			System.out.println("Bug 3. Sorry, shoot me an email explaining how you broke it. Please?");
		}
		
	}

}
